package 泛型;
/*
public class 获取泛型Class对象 {

}
Java中的泛型有着很重要的作用，它能够让我们的数据容器类型安全，避免发生转换异常。不过Java中的泛型也为人诟病，它会在编译中被全部转换成Object对象，也就是泛型
擦除，这造成了诸多不便，除非你能获取泛型的一个实例，否则我们无法直接获取泛型的实际类型。不过JDK依然提供了一个技巧让我们可以获得泛型的具体类型。

【大致原理】
虽然泛型会在字节码编译过程中被擦除，但是Class对象会通过java.lang.reflect.Type记录其实现的接口和继承的父类信息。我们以ArrayList<E>为例：

        ArrayList<String> strings = new ArrayList<>();

        Type genericSuperclass = strings.getClass().getGenericSuperclass();
        // genericInterfaces = java.util.AbstractList<E>
        System.out.println("genericSuperclass = " + genericSuperclass);
虽然我们成功打印出来了泛型的占位符E，但是这并不是我们想要的。我们希望能够获取E的具体类型，也就是String。

让我们回到java.lang.reflect.Type

             GenericArrayType    WildcardType
                        │            │
                        ↓            ↓
                   ┌────────────────────┐
                   │         Type       │
                   └────────────────────┘
                       ↑             ↑
                       │             │
                    Class<T>     ParameterizedType


Type的实现类型

通过上图可以知道Type有四种类型：

1.GenericArrayType 用来描述一个参数泛型化的数组。 
  java.lang.reflect:  
  public interface GenericArrayType extends Type {
      Type getGenericComponentType();
  }

2.WildcardType 用来描述通配符?相关的泛型，包含的?、下界通配符? super E 、上界通配符? extend E。  
  java.lang.reflect:  
  public interface WildcardType extends Type{
  	  Type[] getUpperBounds();
      Type[] getLowerBounds();
  }

3.Class<T>  用来描述类的Class对象。  
  java.lang.Class     
  public final class Class<T> implements Type ...

4.ParameterizedType 用来描述参数化类型。 
  java.lang.reflect   
  public interface ParameterizedType extends Type {
     Type[] getActualTypeArguments();
     Type getRawType();
     Type getOwnerType();
  }

我们再来试一试：

ArrayList<String> strings = new ArrayList<>();

Type genericSuperclass = strings.getClass().getGenericSuperclass();

System.out.println( genericSuperclass instanceof ParameterizedType); // true

System.out.println( genericSuperclass instanceof Class); // false
System.out.println( genericSuperclass instanceof WildcardType); // false
System.out.println( genericSuperclass instanceof GenericArrayType); // false
我们来看看参数化类型能不能获取到具体的类型：

        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        // [E]
        System.out.println("actualTypeArguments = " + Arrays.toString(actualTypeArguments));
ParameterizedType可以帮助我们获取参数类型，可惜依然是E。两种方法为什么都只能获取一个泛型占位符呢？

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 省略

}
这是因为ArrayList实例化时只指定了自己的泛型类型而没有指定父类AbstractList的具体泛型，所以获取到的就是占位符E。我们先来看一个抽象类例子：

  public abstract  class SuperClass<E> {
        private E e;

        protected SuperClass(E e) {
            this.e = e;
        }

        public E get() {
            return this.e;
        }
    }
构建匿名子类实现：

// 实现
 SuperClass<String> superClassInstance = new SuperClass<String>("试一试") {
        };

Type genericSuperclass1 = superClassInstance.getClass().getGenericSuperclass();
//SuperClass<java.lang.String>
System.out.println(genericSuperclass1);
我们通过大括号{}就可以重写实现父类的方法并指定父类的泛型具体类型。我们可以借助这一特性来获取父类的具体泛型类型，我们还拿ArrayList来试试：

ArrayList<String> strings = new ArrayList<String>(){};
Type genericSuperclass = strings.getClass().getGenericSuperclass();
// genericSuperclass = java.util.ArrayList<java.lang.String>
System.out.println("genericSuperclass = " + genericSuperclass);
证明了我们的猜想是对的。那么问题来了如何封装一个工具类？

封装工具类
我们可以借助于抽象类来定义一个获取java.lang.reflect.ParameterizedType的工具类。好在Spring框架中已经提供了一个很好用的实现：

public abstract class ParameterizedTypeReference<T> {
    private final Type type;

    protected ParameterizedTypeReference() {
        Class<?> parameterizedTypeReferenceSubclass = findParameterizedTypeReferenceSubclass(this.getClass());
        // 获取父类的泛型类 ParameterizedTypeReference<具体类型>
        Type type = parameterizedTypeReferenceSubclass.getGenericSuperclass();
        // 必须是 ParameterizedType
        Assert.isInstanceOf(ParameterizedType.class, type, "Type must be a parameterized type");
        ParameterizedType parameterizedType = (ParameterizedType)type;
        // 获取泛型的具体类型  这里是单泛型
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        Assert.isTrue(actualTypeArguments.length == 1, "Number of type arguments must be 1");    // 设置结果
        this.type = actualTypeArguments[0];
    }
 
    // 这个方法开放出去用来调用 来获取泛型的具体Class类型 
    public Type getType() {
        return this.type;
    }
    
    private static Class<?> findParameterizedTypeReferenceSubclass(Class<?> child) {
        Class<?> parent = child.getSuperclass();
        // 如果父类是Object 就没戏了
        if (Object.class == parent) {
            throw new IllegalStateException("Expected ParameterizedTypeReference superclass");
        } else {
            // 如果父类是工具类本身 就返回否则就递归 直到获取到ParameterizedTypeReference
            return ParameterizedTypeReference.class == parent ? child : findParameterizedTypeReferenceSubclass(parent);
        }
    }
}
❝其实 Jackson 、 Gson都有类似的实现。
*/

import java.lang.reflect.Type;

import sun.reflect.generics.repository.ClassRepository;
